package com.boxman

import android.annotation.SuppressLint
import android.content.res.AssetManager

import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableIntState
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp

import com.boxman.BaseGameObject.MoveResult

import java.io.InputStream
import java.util.LinkedList

// 游戏
class Game {
    // 移动结点
    class MoveNode(
        val box: BaseGameObject?,   // 被推动的箱子
        val direction: Int          // 移动方向
    )

    // 状态
    enum class Status {
        PLAYING,    // 游戏中
        STARTING,   // 正在开始
        WON,        // 游戏胜利
        WON_ALL     // 全部通关
    }

    private val mAssetManager: AssetManager             // 资产管理者
    private var mBoxCount: Int                          // 未到达目的地的箱子数
    private lateinit var mLevel: MutableIntState        // 关卡数
    private var mMan: Man? = null                       // 小人
    private lateinit var mMoveCount: MutableIntState    // 移动次数
    private val mMoveList: LinkedList<MoveNode>         // 所有移动结点
    private lateinit var mStatus: MutableState<Status>  // 状态

    companion object {
        lateinit var sBaseGameObjects: SnapshotStateList<SnapshotStateList<BaseGameObject?>>    // 所有游戏物体
    }

    /**
     * 构造方法。
     * @param assetManager  资产管理者
     */
    constructor(assetManager: AssetManager) {
        // 初始化成员
        mAssetManager = assetManager
        mBoxCount = 0
        mMoveList = LinkedList<MoveNode>()
    }

    /**
     * 进入关卡。
     */
    @SuppressLint("DefaultLocale")
    private fun enterLevel() {
        // 小人停止跳舞
        mMan?.stopDancing()

        // 删除所有物体
        for (i in 0 ..< Const.GAME_BOARD_GRID_COUNT_V) {
            for (j in 0 ..< Const.GAME_BOARD_GRID_COUNT_H) {
                sBaseGameObjects[i][j] = null
            }
        }

        // 删除所有移动结点
        mMoveList.clear()

        // 读取关卡文件
        val levelFilePath = String.format(Const.LEVEL_FILE_PATH, mLevel.intValue)
        val reader: InputStream?
        try {
            reader = mAssetManager.open(levelFilePath)
        } catch (e: Exception) {
            e.printStackTrace()
            return
        }

        // 创建所有物体
        var read = -1
        var type: BaseGameObject.Type
        mBoxCount = 0
        for (y in 0 ..< Const.GAME_BOARD_GRID_COUNT_V) {
            var x = 0
            while (x < Const.GAME_BOARD_GRID_COUNT_H) {
                try {
                    read = reader.read()
                } catch (e: Exception) {
                    e.printStackTrace()
                }
                if (read == '0'.code) {
                    x++
                    continue
                }
                if (read == '\r'.code || read == '\n'.code) {
                    continue
                }

                type = BaseGameObject.Type.entries.toTypedArray()[(read - '0'.code)]

                // 创建小人
                if (type == BaseGameObject.Type.MAN) {
                    mMan = Man(mAssetManager, BaseGameObject(mAssetManager, BaseGameObject.Type.BACKGROUND, x, y), type, x, y)
                    sBaseGameObjects[y][x] = mMan
                    x++
                    continue
                }

                // 创建箱子
                if (type == BaseGameObject.Type.BOX) {
                    sBaseGameObjects[y][x] = Box(mAssetManager, BaseGameObject(mAssetManager, BaseGameObject.Type.BACKGROUND, x, y), type, x, y)
                    mBoxCount++
                    x++
                    continue
                }

                // 创建背景、目的地、墙
                sBaseGameObjects[y][x] = BaseGameObject(mAssetManager, type, x, y)
                x++
            }
        }

        // 关闭文件
        try {
            reader.close()
        } catch (e: Exception) {
            e.printStackTrace()
        }

        // 重置成员变量
        mMoveCount.intValue = 0
    }

    /**
     * 创建游戏区域。
     * @param modifier      修饰符
     * @param gameBoardSize 游戏区域尺寸，单位：像素
     */
    @Composable
    fun GameBoard(modifier: Modifier, gameBoardSize: Float) {
        // 初始化成员
        mLevel = remember {
            mutableIntStateOf(1)
        }
        mMoveCount = remember {
            mutableIntStateOf(0)
        }
        mStatus = remember {
            mutableStateOf(Status.STARTING)
        }
        sBaseGameObjects = remember {
            mutableStateListOf<SnapshotStateList<BaseGameObject?>>()
        }
        for (i in 0 ..< Const.GAME_BOARD_GRID_COUNT_V) {
            val subList = remember {
                mutableStateListOf<BaseGameObject?>()
            }
            repeat(Const.GAME_BOARD_GRID_COUNT_H) {
                subList.add(null)
            }
            sBaseGameObjects.add(i, subList)
        }

        // 创建游戏区域
        val gameBoardSizeDp = (gameBoardSize / LocalDensity.current.density).dp
        Canvas(modifier
            .width(gameBoardSizeDp)
            .height(gameBoardSizeDp)
            .background(Const.GAME_BOARD_COLOR)
        ) {
            sBaseGameObjects.forEach {
                it.forEach {
                    it?.draw(this)
                }
            }
        }

        // 进入关卡
        if (mStatus.value == Status.STARTING) {
            this.enterLevel()
            mMan!!.Init()
            mStatus.value = Status.PLAYING
        }
    }

    /**
     * 游戏结束。
     * @param status    游戏状态
     */
    private fun gameOver(status: Status) {
        // 将状态设置为已结束
        mStatus.value = status
    }

    /**
     * 进入指定的关卡。
     * @param level 关卡数
     */
    fun gotoLevel(level: Int) {
        if (level <= Const.LEVEL_COUNT) {
            mLevel.intValue = level
            mStatus.value = Status.STARTING
        }
    }

    /**
     * 进入下一关。
     */
    fun gotoNextLevel() {
        if (mLevel.intValue < Const.LEVEL_COUNT) {
            mLevel.intValue++
            mStatus.value = Status.STARTING
        }
    }

    /**
     * 判断是否胜利。
     * @return 胜利则返回 true，否则返回 false
     */
    private fun isWon(): Boolean {
        return mBoxCount == 0
    }

    /**
     * 获取当前关卡数。
     */
    val level: Int
        get() = mLevel.intValue

    /**
     * 获取移动次数。
     */
    val moveCount: Int
        get() = mMoveCount.intValue

    /**
     * 滑动手势的响应方法。
     * @param direction 方向
     */
    fun onFling(direction: Int) {
        // 如果没在玩，直接返回
        if (mStatus.value != Status.PLAYING) {
            return
        }

        // 移动小人。如果不能移动，则直接返回
        val moveResult = MoveResult()
        val movedBox = mMan?.move(direction, moveResult)
        if (moveResult.result == MoveResult.Result.NO_MOVE) {
            return
        }

        // 增加移动次数
        mMoveCount.intValue++

        // 判断是否胜利
        if (moveResult.result == MoveResult.Result.ARRIVED_ON_DEST) {
            mBoxCount--
            if (this.isWon()) {
                if (mLevel.intValue == Const.LEVEL_COUNT) {
                    this.gameOver(Status.WON_ALL)
                } else {
                    this.gameOver(Status.WON)
                }
                return
            }
        } else if (moveResult.result == MoveResult.Result.LEFT_FROM_DEST) {
            mBoxCount++
        }

        // 加本次移动加入列表
        mMoveList.addLast(MoveNode(movedBox, direction))
    }

    /**
     * 重玩本关。
     */
    fun restart() {
        mStatus.value = Status.STARTING
    }

    /**
     * 获取游戏状态。
     */
    val status: Status
        get() = mStatus.value

    /**
     * 回退，即撤消上一次操作。
     */
    fun undo() {
        if (mMoveCount.intValue == 0) {
            return
        }

        // 计算方向（方向与最后一次移动的方向相反）
        val moveListNode = mMoveList.removeLast()
        var direction: Int = moveListNode.direction
        when (direction) {
            Const.DIRECTION_LEFT -> direction = Const.DIRECTION_RIGHT
            Const.DIRECTION_RIGHT -> direction = Const.DIRECTION_LEFT
            Const.DIRECTION_UP -> direction = Const.DIRECTION_DOWN
            Const.DIRECTION_DOWN -> direction = Const.DIRECTION_UP
        }

        // 移动小人
        val moveResult = MoveResult()
        mMan?.move(direction, moveResult)

        // 移动箱子
        val box: BaseGameObject? = moveListNode.box
        if (box != null) {
            box.move(direction, moveResult)
            if (moveResult.result == MoveResult.Result.LEFT_FROM_DEST) {
                mBoxCount++
            } else if (moveResult.result == MoveResult.Result.ARRIVED_ON_DEST) {
                mBoxCount--
            }
        }

        // 移动次数减1
        mMoveCount.intValue--
    }
}
